#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "command.h"

/*
* NAME:char** cmd_argvs(Cmdline* cmd)
* DESCRIPTION:combine cmd and argc to a new array
* INPUT:Cmdline type  pointer,which point to a Cmdline structure
* RETURN:a array combine the arguments array from Cmdline *cmd and NULL
* like {arg1,arg2...,argN,NULL}                             
*/
char** cmd_argvs(Cmdline* cmd)
{
    int i=1;
    char** argvs=(char**)malloc(sizeof(char*)*(cmd->argc+1));
    argvs[0]=(char*) malloc(sizeof(char)*(strlen(cmd->cmd)+1));
    strncpy(argvs[0], cmd->cmd,strlen(cmd->cmd));
    argvs[0][strlen(cmd->cmd)]='\0';
    while(i<cmd->argc)/*copy old arguments to new array*/
    {
       argvs[i]=(char*) malloc(sizeof(char)*(strlen(cmd->argv[i-1])+1));
       strncpy(argvs[i], cmd->argv[i-1],strlen(cmd->argv[i-1]));
       argvs[i][strlen(cmd->argv[i-1])]='\0';
       i++;
    }
    argvs[i]=NULL;/*the last one must be NULL */
    return argvs;

}

/*
* NAME:int cmd_run(Cmdline* cmd)
* DESCRIPTION:function  runs the command and wait until the command finish
* and return the exectue status.
* INPUT:Cmdline type  pointer,which point to a Cmdline structure
* RETURN: return 1 :execute command successfully.
          return 0 :fail to exectue command.                      
*/
int cmd_run(Cmdline* cmd)
{

    pid_t pid;
    int res=0;
    subType type=cmd_subType(cmd);/*wether the command has io redirection*/
    pid=fork();
    	
        if(pid<0)/*fail to create child process*/
   		{
    		fprintf(stderr, "%s\n",ERR_CREATE_PROCESS);
			exit(EXIT_FAILURE);
    	}
    	else if(0==pid)/*child process code*/
    	{   
    	    if(REDIRECTION==type)/*if cmd has io redirection*/
    	    {
    	       cmd_redirection(cmd,cmd_redirectionType(cmd));
    	    }
    	    else
    	    {
    		    res=execvp(cmd_argvs(cmd)[0],cmd_argvs(cmd));
    		    if(-1==res)/*if exectue fail*/
                {
    		      fprintf(stderr, "%s\n",ERR_EXEC);
			      exit(EXIT_FAILURE);
                }
            }

   		}
    	else/*parent process code*/
    	{
    		waitpid(pid, NULL, 0);/*watit until the process whose process id equals pid finish*/
			sleep(1);
    	}

    return 0;

}


/*
* NAME:int cmd_background(Cmdline* cmd)
* DESCRIPTION:function  runs the command in the background
* and return the exectue status.
* INPUT:Cmdline type  pointer,which point to a Cmdline structure
* RETURN: return 1 :execute command successfully.
          return 0 :fail to exectue command.                      
*/
int cmd_background(Cmdline* cmd)
{

    pid_t pid;
    int res=0;
    subType type=cmd_subType(cmd);
    pid=fork();
    	if(pid<0)/*fail to create child process*/
    	{
    		fprintf(stderr, "%s\n",ERR_CREATE_PROCESS);
			exit(EXIT_FAILURE);
    	}
    	else if(0==pid)/*child process code*/
    	{
  	        if(REDIRECTION==type)/*most  following part same as cmd_run*/
    	    {
    	       cmd_redirection(cmd,cmd_redirectionType(cmd));
    	    }
    	    else
    	    {
    		    res=execvp(cmd_argvs(cmd)[0],cmd_argvs(cmd));
    		    if(-1==res)
                {
    		      fprintf(stderr, "%s\n",ERR_EXEC);
			      exit(EXIT_FAILURE);
                }
            }
    	}
    	else/*parent process code*/
    	{	
    		/*parent process not wait child process*/
			sleep(1);
    	}

    return 0;

}

/*
* NAME:subType cmd_subType(Cmdline* cmd)
* DESCRIPTION:now only need to find out  if command has redirction
* and return the command type.REDIRECTION or PIPE
* We used to plan to add the pipe fuction to our shell,now we remove it
* but still leave the interface.maybe someday I will add this function
* for fun.
* subType is enum varible
* INPUT:Cmdline type  pointer,which point to a Cmdline structure
* RETURN: return REDIRECTION :command has io redirection.
          return PIPE :COMMON command,NO pipe or io redirection .                      
*/
 subType cmd_subType(Cmdline* cmd)
{
	if((NULL != cmd->stdout_dest)||(NULL != cmd->stdin_src))
	{
		return REDIRECTION;
	}
	else
	{
	    /* for now just stand for the normal type no pipe implement
        * now we use pipe as other type of command*/
		return PIPE;

	}
}


/*
* NAME:redirectFlag cmd_redirectionType(Cmdline* cmd)
* DESCRIPTION:return redirection type
* redirectFlag is enum varible
* INPUT:Cmdline type  pointer,which point to a Cmdline structure
* RETURN: return INANDOUT :command has input and output redirection.
          return IN :comamnd only has input redirection.
          return OUT :command only has output redirection.                   
*/
redirectFlag cmd_redirectionType(Cmdline* cmd)
{
	redirectFlag flag;
	if((NULL != cmd->stdout_dest)&&(NULL != cmd->stdin_src))
	{	
		flag=INANDOUT;
	} 
	else if((NULL == cmd->stdout_dest)&&(NULL != cmd->stdin_src)) 
	{
		 flag=IN;
	}
	else if((NULL != cmd->stdout_dest)&&(NULL == cmd->stdin_src))
	{
		flag=OUT;
	}

	return flag;
}

/*
* NAME:int cmd_redirection(Cmdline* cmd,redirectFlag reflag)
* DESCRIPTION:processes the command with io redirection
* and return the exectue status.
* INPUT:cmd-Cmdline type  pointer,which point to a Cmdline structure
*       reflag:redirectFlag type, stand for the type of redirection
* RETURN: return 1 :execute command successfully.
          return 0 :fail to exectue command.                      
*/
int cmd_redirection(Cmdline* cmd,redirectFlag reflag)
{

	int res=0;
	redirectFlag flag=reflag;
	char* in=cmd->stdin_src;
	char* out=cmd->stdout_dest;    
    int input,output;
	setbuf(stdin, NULL);

     
		switch(flag)
		{	
			case INANDOUT:
			input=open(in,O_RDONLY);/*open input source file,set readonly mode*/
    		if(input<0)
    		{
    			fprintf(stderr, "%s\n", ERR_CREATE_FILE);
    			exit(EXIT_FAILURE);
    		}

    		/*open outpud destination file,
			*flag O_RDWR:the open mode allows writing and reading
			*flag O_CREAT:If  the file does not exist it will be created
			*flag O_TRUNC:If  the  file  already exists and already set  O_RDWR
			*it  will  be truncated to length 0
			*S_IRWXU:user (file owner) has  read,  write  and  execute permission
			*only used when set flag O_CREAT
    		*/
    		output=open(out, O_RDWR|O_CREAT|O_TRUNC,S_IRWXU);
    		if(output<0)
    		{
    			fprintf(stderr, "%s\n",ERR_CREATE_FILE );
    			exit(EXIT_FAILURE);
    		}
    		/*replace standard input with input file*/
    		dup2(input,STDIN_FILENO);
    		/*replace standard out with out file*/
    		dup2(output,STDOUT_FILENO);
    		/*close all used files*/
    		close(input);
  			close(output);
    		
    		break;

    		case IN:
    		input=open(in,O_RDONLY);
    		if(input<0)
    		{
    			fprintf(stderr, "%s\n", ERR_CREATE_FILE);
    			exit(EXIT_FAILURE);
    		}
    		/*replace standard input with input file*/
    		dup2(input,STDIN_FILENO);
    		/*close input file*/
    		close(input);

    		break;
    		case OUT:
    		output=open(out, O_RDWR|O_CREAT|O_TRUNC,S_IRWXU);
    		if(output<0)
    		{
    			fprintf(stderr, "%s\n",ERR_CREATE_FILE );
    			exit(EXIT_FAILURE);
    		}
    		/*replace standard out with out file*/
    		dup2(output,STDOUT_FILENO);
    		/*close out file*/
  			close(output);
    		break;
    		default:
    		break;
    	}
       execvp(cmd_argvs(cmd)[0],cmd_argvs(cmd));
       if(-1==res)
       {
          fprintf(stderr, "%s\n",ERR_EXEC);
          exit(EXIT_FAILURE);
        }
        exit(0);
}
